package com.woniuxy.special.interceptor;

import com.woniuxy.special.exception.JwtException;
import com.woniuxy.special.exception.ManagerException;
import com.woniuxy.special.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;


/**
 * 在解析token时可能会抛出三类异常：
 * <p>
 * - MalformedJwtException
 * <p>
 * 格式错误异常
 * <p>
 * - SignatureException
 * <p>
 * 签名异常
 * <p>
 * - ExpiredJwtException
 * <p>
 * JWT过期
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("==================================");
        //排除OPTIONS请求
        if (request.getMethod().equals("OPTIONS")) {
            return true;
        }
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            throw new ManagerException(1111, "尚未登录，先登录");
        }

        //校验令牌合法性
        try {
            JwtUtil.parseToken(token); //校验过期时间，格式错误，签名错误，会抛出相应的异常
        } catch (MalformedJwtException e) {
            throw new JwtException(401, "令牌无效");
        } catch (SignatureException e) {
            throw new JwtException(401, "令牌无效");
        } catch (ExpiredJwtException e) {
            //判断短令牌是否过期
            if (Boolean.FALSE.equals(redisTemplate.hasKey("accessToken:expire:" + token))) { //过期
                synchronized (token.intern()) {
                    //判断防止重复续期
                    if (Boolean.FALSE.equals(redisTemplate.hasKey("accessToken:expire:" + token))) {
                        //判断长令牌过期时间剩余的是否够1分钟
                        if (redisTemplate.getExpire(token) < 60) { //获取剩余过期时间
                            throw new ManagerException(401, "令牌过期");
                        }
                        //获取并解析长令牌
                        String s = redisTemplate.opsForValue().get(token);
                        Claims claims = JwtUtil.parseToken(s);
                        //删除之前存储的长token
                        new Thread(() -> {
                            try {
                                Thread.sleep(10 * 1000);
                                redisTemplate.delete(token);
                            } catch (InterruptedException e2) {
                                e2.printStackTrace();
                            }
                        }).start();
                        //生成短令牌5分钟
                        String accessToken = JwtUtil.createToken(claims, 5);
                        //生成长令牌1天
                        String refreshToken = JwtUtil.createToken(claims, 24 * 60);
                        //保存短token的过期时间
                        redisTemplate.opsForValue().set("accessToken:expire:" + accessToken, "", 5, TimeUnit.MINUTES);
                        //保存长token到redis
                        redisTemplate.opsForValue().set(accessToken, refreshToken, 24 * 60, TimeUnit.MINUTES);

                        //设置响应头
                        response.setHeader("token", accessToken);
                        //在跨域请求访问时 必须公开该请求头 前端才能获取到
                        response.setHeader("Access-Control-Expose-Headers", "token");

                        //给短令牌续期
                        redisTemplate.opsForValue().set("accessToken:expire:" + token, "", 5, TimeUnit.MINUTES);
                    }
                }
            }
        }
        return true;
    }


}
